當初接觸到rxjs 這套以流的方式處理事件及資料的庫時,覺得非常新奇,便嘗試自己從頭寫了一個rxjs + vuerx + vue 支援觸控端、響應式的圖片剪裁元件,取名叫vuejs -clipper 並發布至npm。
算是一個簡單的元件,這篇主要介紹下當初的一些有趣的思考方向,而非程式方面如何實作。
完整的程式碼、使用範例都在連結裡,有興趣、喜歡的話可以使用看看或給個星當做鼓勵 :)
前端使用Vue,要在Vue Component中使用rxjs的話,vuerx這個Vue的插件(Plugin)提供兩者很好的結合,讓Vue component多了個subscriptions選項可定義 rxjs 的 subject 和 Observable 等。
以及提供v-stream
,和v-on
一樣監聽Dom事件,但是以rxjs Observable流式來處理。
元件使用Vue SFC方式開發,最後用@vue/cli 打包成umd擋案可用script引入。
(以下圖多注意)
其實已經有許多圖片剪裁元件的庫了,像是這個cropper.js,功能應該非常強大齊全(沒有使用過...汗顏),還有結合cropper.js和Vue component的vue-cropperjs等等.. ....
這邊打個岔,先來定義一些名詞以方便我後面繼續講下去。 。 。
這張圖以vuejs-clipper為例
如果剛剛有玩一下cropper.js的話可以發現,他的剪裁框和圖片都是可以縮放和移動的。
不過當初我的想法是,其中一個往左移動,不就相對於另一個元素往右移動了;其中一個元素縮小,就相對於另一個元素放大了啊。
所以我認為圖片或剪裁框,選擇其中一個進行縮放和移動就好。
基於這個原則我做了兩種不同的元件,取名叫 clipper-basic(示例) 和 clipper-fixed(示例)。
手指/滑鼠在剪裁元件上只能縮放移動剪裁框,但可以另外綁定屬性來控製圖片旋轉和放大。
手指/滑鼠在剪裁元件上只能縮放移動圖片,但可以另外綁定屬性來控製圖片旋轉。
接下來介紹一些設計、優化的重點。
剪裁框、圖片都是剪裁元件的子元素,長寬(width, height)、位置(top/bottom, left/right),需要用相對外框的百分比來表示,否則螢幕大小一變就會跑版。
如果能使用transform: translate 來替代layout: top/bottom, left/right 的話效能會更好,但當初開始寫的時後我剪裁框的位置和長寬並非一起計算,render的順序不一致導致我用translate的話常常會跑版,後來就乾脆用top/bottom, left/right 來表示位置了。 。 。
如何實作拖弋剪裁框相信大家都會的:
紅點是一開始點擊的位置(基準點,跟著藍色框移動)
按著滑鼠/手指從P1移動到P2
移動框(藍色框)移動後位置(以top, left表示的話):top 即是 y2 - y1,left 則是 x2 - x1
當然我們還要判斷藍色框不能超出黑色外框, 計算出來的 top/left 需要介於 min top/left 和 max top/left 之間。
上圖演示一個例子,當鼠標移動到藍點1 時藍色框並不會移動因為已經抵達邊界,但如果使用者依然按著滑鼠/手指並移回藍點2,會發現藍色框還是沒有移動,這是因為基準點紅點並沒有改變,造成在灰色斜線的區域移動滑鼠,藍色框都不會移動,是一個不太好的體驗。
解決方法:當藍色框抵達邊界,但滑鼠/手指仍在移動時,基準點跟著改變且不超出邊界,這樣就不會有滑鼠白白移動的感覺。
以下示範滑鼠從P1點擊移動到P2,P3,P4,紅色基準點跟著改變,這樣當鼠標一往回移動藍色框就可以跟著移動。
拖弋圖片的話就比較沒有這個問題,因為圖片是可以超出邊界的。
縮放剪裁框其實就是判斷一開始滑鼠點在框上的哪個方位,就使用對秤點當作基準點,剪裁框以該點不動來縮放。
可以發現Cropper.js是有八個基準點可以縮放的:
但當初的 clipper-basic 設計成剪裁框是可以縮放成任意比例的,因此邊上的基準點就不需要了,只做了四個基準點:
其實在任意比例(長寬比例不限) 下這樣設計是沒問題的,但後來想要加上限制比例的設定就比較尷尬了,可以去展示把ratio 選項打勾並縮放剪裁框試試,因為少了邊上的基準點,在水平或垂直縮放時用起來有些奇怪,但是對角縮放就不會。
正常來說圖片剪裁元件可以設定外觀長寬,但一開始clipper-basic 並沒有要做圖片縮放,並想把這個元件做成像一個 元素,根據圖片自動調整比例,但是只做了根據寬度調整高度,而沒有設定高度來調整寬度的選項。 。 。
這就造成一個問題,假設圖片剪裁元件設定 width: 500px,當上傳一個很長的圖片時,網頁的佈置會被拉的很長。
算是設計上的疏失,可能的解決辦法寫在這裡。
clipper-fixed 的話是有設定剪裁比例的,長寬比則是看剪裁比例相同。
clipper-fixed 縮放圖片,綁定滾輪/兩指觸控事件,會發現如果使用固定比例縮放(假設每次縮放0.1倍好了),在圖片較小時會覺得縮放的很快,圖片很大時縮放則感覺非常緩慢。
因此需要用線性方式來調整縮放比例:
(下圖只是示例,非真正的比例)
縮放率要隨著圖片的大小增加,公式的話…一元、二元應該都是可以的,調整到自己感覺最舒適的參數。
想要表現的像一個,因此有src屬性,接受一個圖片的 URL。輸出則是一個canvas element,使用者可將canvas繪製到其他地方,或者轉成image元素、Blob、URL等,這些操作是比較消耗效能和時間的。
設計這個元件(而非函式庫)就是想要間單一點,單純控制UI,不介入一些上傳、轉換圖片的處理。
總結一下這次作這個元件可以改善的地方,以後(應該是不會有以後.w.)可以改進:
個人認為 clipper-fixed 這種操作方式是最通用也最沒毛病的,但我也不懂一些UI/UX啥的,不知道怎樣使用者操作比較順手。 。 。
雖然說是用 vue + vuerx + rxjs 但本文幾乎沒有講到這三者的時作和程式碼,因本人也算是剛接觸的生手,尚在摸索,就沒有對這幾個庫作講解。
就先介紹到這,希望大家會喜歡。